package com.open.rbac.service.sys.impl;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.ExampleMatcher;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import com.open.center.common.constant.OpenField;
import com.open.center.common.exception.OpenException;
import com.open.center.common.util.CopyUtils;
import com.open.rbac.constant.MenuType;
import com.open.rbac.model.dto.sys.MenuDto;
import com.open.rbac.model.entity.sys.Menu;
import com.open.rbac.model.vo.sys.MenuVo;
import com.open.rbac.service.sys.IMenuService;
import com.open.rbac.service.sys.IRoleMenuRelService;
import com.open.rbac.service.sys.repository.MenuRepository;


/**
 * 菜单
 *
 * @author Riche's
 * @since 2022/8/24
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class MenuServiceImpl implements IMenuService {

    private final MenuRepository menuRepository;

    private final IRoleMenuRelService roleMenuRelService;

    @Override
    public MenuDto create(MenuVo menuVo) {
        Menu menu = CopyUtils.copy(menuVo, Menu.class);
        Optional<Menu> optional = menuRepository.findById(menuVo.getId());
        optional.ifPresent(m -> {
            throw new OpenException(String.format("菜单ID[%s, %s]已存在", m.getId(), m.getName()));
        });
        if (!StringUtils.hasText(menu.getParentId())) {
            menu.setParentId("0");
        }
        if (menu.getSort() == null) {
            menu.setSort(1);
        }
        menuRepository.save(menu);
        return CopyUtils.copy(menu, MenuDto.class);
    }

    @Override
    public MenuDto update(MenuVo menuVo) {
        if (MenuType.FUNCTION == menuVo.getType()) {
            if (!StringUtils.hasText(menuVo.getMethod())
                || !StringUtils.hasText(menuVo.getUrl())) {
                log.error("菜单类型为：{}, 'Url'与'Method'为必填项", menuVo.getType());
                throw new OpenException(menuVo.getType() + " 类型的菜单，Url和请求方法为必填项");
            }
        } else if (MenuType.MENU == menuVo.getType()) {
            log.info("{}, {}", menuVo.getType().getName(), menuVo.getId());
            if (!StringUtils.hasText(menuVo.getRouterComponent())) {
                log.error("菜单类型为：{}, 'routerName'为必填项", menuVo.getType());
                throw new OpenException(menuVo.getType() + " 类型的菜单，路由组件为必填项");
            }
        }
        if (!StringUtils.hasText(menuVo.getParentId())) {
            menuVo.setParentId("0");
        }
        Optional<Menu> optional = menuRepository.findById(menuVo.getId());
        return optional.map(menu -> {
            CopyUtils.copyNonNull(menuVo, menu);
            menuRepository.save(menu);
            return CopyUtils.copy(menu, MenuDto.class);
        }).orElse(null);
    }

    @Override
    @Transactional
    public void deleteById(String id) {
        Optional<Menu> optional = menuRepository.findById(id);
        optional.ifPresent(m -> {
            Menu example = new Menu();
            example.setParentId(id);
            List<Menu> menus = menuRepository.findAll(Example.of(example));
            if (!CollectionUtils.isEmpty(menus)) {
                log.error("[{}]{}下含有子菜单，不能直接删除", m.getType().getName(), m.getName());
                throw new OpenException(String.format("[%s]%s下含有子菜单，不可删除", m.getType().getName(), m.getName()));
            }
            roleMenuRelService.deleteReallyByMenuId(id);
            menuRepository.deleteById(id);
        });
    }

    @Override
    public void deleteReally(String id) {
        log.info("物理删除菜单：{}", id);
        roleMenuRelService.deleteReallyByMenuId(id);
        menuRepository.deleteReallyById(id);
    }

    @Override
    public void enable(String id) {
        menuRepository.updateEnable(id, true);
    }

    @Override
    public void disable(String id) {
        menuRepository.updateEnable(id, false);
    }

    @Override
    public MenuDto findById(String id) {
        Optional<Menu> optional = menuRepository.findById(id);
        return optional.map(menu -> CopyUtils.copy(menu, MenuDto.class)).orElse(null);
    }

    @Override
    public List<MenuDto> listByName(String name) {
        return CopyUtils.listCopy(menuRepository.findByName(name), MenuDto.class);
    }

    @Override
    public List<MenuDto> listByUserId(String userId) {
        List<Menu> menus = menuRepository.findByUserId(userId);
        return CopyUtils.listCopy(menus, MenuDto.class);
    }

    @Override
    public List<MenuDto> listByRoleId(String roleId) {
        List<Menu> menus = menuRepository.findByRoleId(roleId);
        return CopyUtils.listCopy(menus, MenuDto.class);
    }

    @Override
    public List<MenuDto> tree(MenuVo menuVo) {
        // 1. 一次排序全量查询
        List<Menu> allMenus = menuRepository.findAll(Sort.by(Sort.Direction.ASC, OpenField.SORT.getId()));
        if (CollectionUtils.isEmpty(allMenus)) {
            return List.of();
        }

        // 2. 构建 id -> Dto映射, 提供o(1)复杂度的快速查找
        Map<String, MenuDto> mapMenuDto = allMenus.stream()
            .map(menu -> {
                MenuDto dto = CopyUtils.copy(menu, MenuDto.class);
                dto.setChildren(new ArrayList<>()); // 初始化children集合
                return dto;
            })
            .collect(Collectors.toMap(MenuDto::getId, Function.identity()));

        // 3. 提供MenuVo参数按'name','authKey', 'enable' 进行筛选
        List<MenuDto> filteredMenuDtos = filterMenuDtos(menuVo, mapMenuDto);

        // 4. 遍历筛选结果并补全其父节点
        Map<String, MenuDto> treeMenuMap = new HashMap<>();
        for (MenuDto menuDto : filteredMenuDtos) {
            MenuDto currentMenuDto = mapMenuDto.get(menuDto.getId());
            while (currentMenuDto != null && !treeMenuMap.containsKey(currentMenuDto.getId())) {
                treeMenuMap.put(currentMenuDto.getId(), currentMenuDto);
                currentMenuDto = mapMenuDto.get(currentMenuDto.getParentId());
            }
        }

        // 5. 构建树形结构并按'enable'条件进行父节点过滤
        List<MenuDto> treeList = new ArrayList<>();
        for (MenuDto menuDto : treeMenuMap.values()) {
            if (Boolean.TRUE.equals(menuVo.getEnable()) && !menuDto.isEnable()) {
                continue;
            }
            String parentId = menuDto.getParentId();
            MenuDto parentDto = treeMenuMap.get(parentId);
            if (parentDto != null) {
                parentDto.getChildren().add(menuDto);
            } else {
                treeList.add(menuDto);
            }
        }
        // 6. 树排序。
        sortTree(treeList);
        return treeList;
    }

    /**
     * 按条件过滤菜单
     */
    private List<MenuDto> filterMenuDtos(MenuVo menuVo, Map<String, MenuDto> mapMenuDto) {
        List<MenuDto> filteredMenuDtos = mapMenuDto.values().stream()
            .filter(dto -> {
                // 按名称模糊匹配
                boolean nameMatch = !StringUtils.hasText(menuVo.getName()) ||
                    (dto.getName() != null && dto.getName().contains(menuVo.getName()));
                // 按权限标识模糊匹配
                boolean authKeyMatch = !StringUtils.hasText(menuVo.getAuthKey()) ||
                    (dto.getAuthKey() != null && dto.getAuthKey().contains(menuVo.getAuthKey()));
                // 按启用状态精确匹配
                boolean enableMatch = menuVo.getEnable() == null || dto.isEnable() == menuVo.getEnable();
                return nameMatch && authKeyMatch && enableMatch;
            }).toList();

        if (CollectionUtils.isEmpty(filteredMenuDtos)) {
            return List.of();
        }
        return filteredMenuDtos;
    }


    /**
     * 递归树排序
     */
    private void sortTree(List<MenuDto> menuDtos) {
        if (CollectionUtils.isEmpty(menuDtos)) {
            return;
        }
        // 按'sort'升序
        menuDtos.sort(Comparator.comparing(MenuDto::getSort, Comparator.nullsLast(Integer::compareTo)));
        // 递归子节点列表
        menuDtos.forEach(node -> sortTree(node.getChildren()));
    }

    public Example<Menu> example(MenuVo menuVo) {
        Menu menu = new Menu();
        if (menuVo != null) {
            menu.setName(menuVo.getName());
            menu.setAuthKey(menuVo.getAuthKey());
            menu.setEnable(menuVo.getEnable());
        }
        ExampleMatcher matcher = ExampleMatcher.matching()
            .withMatcher("name", ExampleMatcher.GenericPropertyMatcher::contains)
            .withMatcher("authKey", ExampleMatcher.GenericPropertyMatcher::contains)
            .withMatcher("enable", ExampleMatcher.GenericPropertyMatcher::exact)
            .withIgnoreCase(true);
        return Example.of(menu, matcher);
    }

}
